package stack

import (
	"errors"
	"fmt"
	"strings"
)

type tArrayStack struct {
	items []interface{}
	capacity int
	top int
	version int64
}

var gEmptyStackError = errors.New("empty stack")

func NewArrayStack(capacity int) IStack {
	if capacity < 0 {
		capacity = 0
	}

	return &tArrayStack{
		items: make([]interface{}, capacity),
		capacity: capacity,
		top: -1,
	}
}

func (me *tArrayStack) Size() int {
	return me.top + 1
}

func (me *tArrayStack) IsEmpty() bool {
	return me.Size() <= 0
}

func (me *tArrayStack) IsNotEmpty() bool {
	return !me.IsEmpty()
}

func (me *tArrayStack) Push(value interface{}) {
	me.ensureCapacity(me.Size() + 1)
	me.top++
	me.items[me.top] = value
	me.version++
}

func (me *tArrayStack) ensureCapacity(size int) {
	if me.capacity >= size {
		return
	}

	for ;me.capacity<size; {
		me.capacity = maxInt(me.capacity*2, me.capacity+1)
	}

	newItems := make([]interface{}, me.capacity)
	copy(newItems, me.items)
	me.items = newItems
}

func maxInt(x, y int) int {
	if x >= y {
		return x
	}
	return y
}

func (me *tArrayStack) Pop() (error, interface{}) {
	if me.IsEmpty() {
		return gEmptyStackError, nil
	}

	it := me.items[me.top]
	me.items[me.top] = nil
	me.top--

	me.version++
	return nil, it
}

func (me *tArrayStack) Peek() (error, interface{}) {
	if me.IsEmpty() {
		return gEmptyStackError, nil
	}

	return nil, me.items[me.top]
}

func (me *tArrayStack) Iterator() IStackIterator {
	return newStackIterator(me)
}

func (me *tArrayStack) String() string {
	itemStrings := make([]string, me.Size())
	for i:=0;i<me.Size();i++ {
		itemStrings[i] = fmt.Sprintf("%v", me.items[i])
	}

	return fmt.Sprintf("c=%v,t=%v,v=%v,items:%s", me.capacity, me.top, me.version, strings.Join(itemStrings, ","))
}
